package org.dtrevien.yuml.parser.impl;

import java.util.ArrayList;
import java.util.List;

import org.dtrevien.metamodel.java.Attribut;
import org.dtrevien.metamodel.java.Classe;
import org.dtrevien.metamodel.java.ClasseAggregationReference;
import org.dtrevien.metamodel.java.ClasseCompositionReference;
import org.dtrevien.metamodel.java.ClasseExtendReference;
import org.dtrevien.metamodel.java.ClasseImplementReference;
import org.dtrevien.metamodel.java.Import;
import org.dtrevien.metamodel.java.Methode;
import org.dtrevien.metamodel.java.ObjectList;
import org.dtrevien.metamodel.java.Relationship;
import org.dtrevien.metamodel.java.Systeme;
import org.dtrevien.metamodel.java.Value;
import org.dtrevien.metamodel.java.Visibility;
import org.dtrevien.yuml.model.Cardinality;
import org.dtrevien.yuml.model.Member;
import org.dtrevien.yuml.model.Param;
import org.dtrevien.yuml.parser.YumlEventListener;


/**
 * Products a JavaModel from a Yuml specification
 * 
 * @author davidtrevien
 *
 */
public class YumlEventListenerImpl implements YumlEventListener {

	protected Systeme system;
	protected Classe c;
	protected Attribut a0, a1;
	protected Cardinality card0, card1;
	protected String label0, label1;
	
	public YumlEventListenerImpl() {
		this.system = new Systeme();
	}
	
	private String unCapitalize(String str) {
		return str.substring(0, 1).toLowerCase() + str.substring(1);
	}
	
	private String card2HumanReadable(Cardinality c) {
		switch (c) {
		case n: return "*";
		case one: return "1";
		case zero_n: return "0..*";
		case zero_one: return "0..1";
		default:
			return null;
		}
	}
	
	@Override
	public void startYUML() {
		System.err.println("start");
	}

	@Override
	public void endYUML() {
		System.err.println("end");
	}

	@Override
	public void onClassEvent(String name) {
		System.err.println("Class : "+name);
		this.c = new Classe(name, "");
		this.system.add(this.c);
	}

	
	@Override
	public void onClassAttributeEvent(String fartherClass, String name, String type, String visibility) {
		System.err.println("Class Attribute : "+name);
		Attribut e = new Value(new Classe(type, ""), name, Relationship.CLASS_ATTRIBUTE,
				Visibility.string2Visibility(visibility));
		this.system.getClasse(fartherClass).getAttributs().add(e);
	}


	@Override
	public void onClassMethodEvent(String fartherClass, String name, String returnType, List<Param> params,  String visibility) {
		System.err.println("Class Method : "+name+" "+params.size());
		List<Attribut> attrList = new ArrayList<Attribut>();
		for (Param param : params) {
			Attribut e = new Value(new Classe(param.getType(), ""), param.getName(), Relationship.METHOD_PARAM, null);
			attrList.add(e);
		}
		Methode e = new Methode(new Classe(returnType, ""), name, Visibility.string2Visibility(visibility), attrList);
		this.system.getClasse(fartherClass).getMethods().add(e);
	}
	
	@Override
	public void onNoteEvent(String text) {
		System.err.println("Note : "+text);
		this.system.getNotes().add(text);
	}

	@Override
	public void onAggregationEvent(String fartherClassName, String childClassName) {
		System.err.println("Aggreagation "+fartherClassName+" <|-- "+childClassName);
		this.a0 = new ClasseAggregationReference(this.system.getClasse(childClassName), childClassName, Relationship.AGGREGATION);
		//this.a1 = this.a0;
		this.system.getClasse(fartherClassName).getAttributs().add(this.a0);
	}

	@Override
	public void onCompositionEvent(String fartherClassName, String childClassName) {
		System.err.println("Composition "+fartherClassName+" <|-- "+childClassName);
		this.a0 = new ClasseCompositionReference(this.system.getClasse(childClassName), childClassName, Relationship.COMPOSITION);
		//this.a1 = this.a0;
		this.system.getClasse(fartherClassName).getAttributs().add(this.a0);
	}

	@Override
	public void onCardinalityEvent(Cardinality c, String fartherClass, String childClass) {
		System.err.println("Cardinality : "+c);
		if (this.card0 == null) {
			this.card0 = c;
		}
		else {
			this.card1 = c;
		}
		
		if (this.a0 != null && (this.card0.equals(Cardinality.zero_n) || this.card0.equals(Cardinality.n))) {
			this.a0 = new ObjectList(this.a0.getType(), this.a0.getName(), this.a0.getRelationship(), null);
			this.system.getClasse(fartherClass).addImport(new Import("java.util.List"));
			this.system.getClasse(fartherClass).getAttributs().set(
					this.system.getClasse(fartherClass).getAttributs().size()-1, 
					this.a0);
		}
		
		if (this.a1 != null && this.card1 != null && (this.card1.equals(Cardinality.zero_n) || this.card1.equals(Cardinality.n))) {
			this.a1 = new ObjectList(this.a1.getType(), this.a1.getName(), this.a1.getRelationship(), null);
			this.system.getClasse(fartherClass).addImport(new Import("java.util.List"));
			this.system.getClasse(fartherClass).getAttributs().set(
					this.system.getClasse(fartherClass).getAttributs().size()-1, 
					this.a1);
		}
		
		if (this.a0 != null) {
			if (this.card1 != null)
				this.a0.setCard1(card2HumanReadable(this.card1));
			if (this.card0 != null)
				this.a0.setCard0(card2HumanReadable(this.card0));
		}
		
		if (this.a1 != null) {
			if (this.card0 != null)
				this.a1.setCard1(card2HumanReadable(this.card0));
			if (this.card1 != null)
				this.a1.setCard0(card2HumanReadable(this.card1));
		}
	}

	@Override
	public void onInheritanceEvent(String farther, String child) {
		System.err.println("Inheritance "+farther+" "+child);
		ClasseExtendReference ext = new ClasseExtendReference(this.system.getClasse(farther), 
				this.system.getClasse(farther).getName(),Relationship.INHERITANCE, null); 
		this.system.getClasse(child).getExtends().add(ext);
		this.system.getClasse(farther).setAsAbstract(true);
	}

	@Override
	public void onImplementationEvent(String farther, String child) {
		System.err.println("Implementation "+farther+" "+child);
		ClasseImplementReference ext = new ClasseImplementReference(this.system.getClasse(farther), 
				this.system.getClasse(farther).getName(),Relationship.IMPLEMENTATION, null); 
		this.system.getClasse(child).getImplementations().add(ext);
		this.system.getClasse(farther).setAsAbstract(true);
	}

	@Override
	public void onDependenciesEvent() {
		System.err.println("Dependencies");
		
	}

	@Override
	public void onDirectionalEvent(String fartherClassName, String childClassName) {
		System.err.println("Directional : "+fartherClassName+" ---> "+childClassName);
		this.a0 = new Value(this.system.getClasse(childClassName), unCapitalize(childClassName), Relationship.DIRECT, null);
		this.a1 = this.a0;
		this.system.getClasse(fartherClassName).getAttributs().add(this.a0);
	}

	@Override
	public void onBidirectionalEvent(String fartherClassName, String childClassName) {
		System.err.println("Bidirectional : "+fartherClassName+" <--> "+childClassName);
		this.a0 = new Value(this.system.getClasse(childClassName), unCapitalize(childClassName), Relationship.BIDIRECT, null);
		this.system.getClasse(fartherClassName).getAttributs().add(this.a0);
		
		this.a1 = new Value(this.system.getClasse(fartherClassName), unCapitalize(fartherClassName), Relationship.BIDIRECT, null);
		this.system.getClasse(childClassName).getAttributs().add(this.a1);
	}

	@Override
	public void onLabelsEvent(String label, Member member) {
		System.err.println("Label : "+label+" "+member);
		if (member.equals(Member.LEFT)) {
			this.a0.setName(label);
		}
		else {
			if (this.a1 != null) this.a1.setName(label);
		}
	}

	public Systeme getSysteme() {
		return this.system;
	}

	@Override
	public void onNewLineEvent() {
		System.err.println("");
		
		//Reset class variables
		this.card0 = this.card1 = null;
		this.label0 = this.label1 = null;
		this.a0 = this.a1 = null;
	}

	
}
